# Title: Cut Composition Cost # Description: Turns off transparency and window animations when the desktop compositor # is measurably loading the graphics card # # No double quotes and no semicolons are used anywhere in this file. The agent # script runner wraps content in double quotes, so an inner double quote ends # the wrapper early and any following text is run as a command. This file is # checked by resources/build/validate_script_quoting.py. # Gated on the desktop compositor itself being a measured consumer, so this never # fires as a cosmetic change for no reason # Settings persist until reversed, so this action needs user approval # Engine points on one engine of one adapter before DWM is treated as a real consumer $dwmCompositionThreshold = 10 # Durable audit sink for the per user registry writes this lever performs # The event log write is best effort - a blocked or missing source degrades to # standard output and never stops the remediation. Swap this one function if the # fleet standard is a different sink $dexAuditSource = 'DexGpuRemediation' function Write-RemediationEvent { param( [int]$EventId, [string]$EntryType, [string]$Message ) Write-Output $Message try { if (-not [System.Diagnostics.EventLog]::SourceExists($dexAuditSource)) { New-EventLog -LogName 'Application' -Source $dexAuditSource -ErrorAction Stop } Write-EventLog -LogName 'Application' -Source $dexAuditSource -EventId $EventId -EntryType $EntryType -Message $Message -ErrorAction Stop } catch { Write-Output ('audit: event log write failed - {0}' -f $_.Exception.Message) } } $engineSampleSet = Get-Counter -Counter '\GPU Engine(*)\Utilization Percentage' -SampleInterval 1 -MaxSamples 2 -ErrorAction SilentlyContinue if ($null -eq $engineSampleSet) { Write-Output 'skip: gpu engine counters unavailable on this device' return } # Sum engine indexes that share a physical adapter and an engine type, then take the # heaviest single engine per process so the unit stays absolute on a multi adapter box $engineTotals = @{} foreach ($engineSample in $engineSampleSet[-1].CounterSamples) { if ($engineSample.CookedValue -le 0) { continue } if ($engineSample.InstanceName -notmatch 'pid_(\d+)_.*phys_(\d+)_eng_\d+_engtype_(.+)$') { continue } $engineBucketKey = '{0}|{1}|{2}' -f $Matches[1], $Matches[2], $Matches[3] if ($engineTotals.ContainsKey($engineBucketKey)) { $engineTotals[$engineBucketKey] = $engineTotals[$engineBucketKey] + $engineSample.CookedValue } else { $engineTotals[$engineBucketKey] = $engineSample.CookedValue } } $peakLoadByProcessId = @{} foreach ($engineBucketKey in $engineTotals.Keys) { $bucketOwnerProcessId = [int]($engineBucketKey -split '\|')[0] $bucketLoad = $engineTotals[$engineBucketKey] if ((-not $peakLoadByProcessId.ContainsKey($bucketOwnerProcessId)) -or ($bucketLoad -gt $peakLoadByProcessId[$bucketOwnerProcessId])) { $peakLoadByProcessId[$bucketOwnerProcessId] = $bucketLoad } } $dwmProcess = Get-Process -Name 'dwm' -ErrorAction SilentlyContinue | Select-Object -First 1 if ($null -eq $dwmProcess) { Write-Output 'skip: dwm process not found' return } $dwmLoad = 0 if ($peakLoadByProcessId.ContainsKey($dwmProcess.Id)) { $dwmLoad = $peakLoadByProcessId[$dwmProcess.Id] } if ($dwmLoad -le $dwmCompositionThreshold) { Write-Output ('skip: dwm at {0} engine points, below threshold of {1}' -f [math]::Round($dwmLoad, 0), $dwmCompositionThreshold) return } # The sensor runs as SYSTEM, so HKCU points at the SYSTEM profile and is useless for # per-user graphics settings. Resolve the console user to a SID and address their # hive directly through HKEY_USERS instead. # Translate can throw on a host with no interactive user or with a name that no longer # maps to an account (RDS and fast user switching), so it is guarded and this lever # fails closed rather than throwing or writing to the wrong hive. # Limitation to revisit: Win32_ComputerSystem.UserName returns a single console user, # so on a multi session host this targets that one user. A per session owner lookup is # the correct long term source and is a separate change $interactiveUserRegistryRoot = $null $consoleUserName = (Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue).UserName if (-not [string]::IsNullOrWhiteSpace($consoleUserName)) { try { $ntAccount = New-Object System.Security.Principal.NTAccount($consoleUserName) $consoleUserSid = $ntAccount.Translate([System.Security.Principal.SecurityIdentifier]).Value if (Test-Path ('Registry::HKEY_USERS\{0}' -f $consoleUserSid)) { $interactiveUserRegistryRoot = 'Registry::HKEY_USERS\{0}' -f $consoleUserSid } } catch { Write-Output ('skip: could not resolve console user {0} to a sid' -f $consoleUserName) } } if ($null -eq $interactiveUserRegistryRoot) { Write-Output 'skip: no interactive user hive found' return } $appliedAnyChange = $false # DWM watches the Personalize key and reacts to a change notification, so a # transparency change written to the user hive applies without a sign out $personalizeKeyPath = '{0}\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize' -f $interactiveUserRegistryRoot if (Test-Path $personalizeKeyPath) { $currentTransparency = (Get-ItemProperty -Path $personalizeKeyPath -Name 'EnableTransparency' -ErrorAction SilentlyContinue).EnableTransparency if ($currentTransparency -ne 0) { Set-ItemProperty -Path $personalizeKeyPath -Name 'EnableTransparency' -Value 0 -Type DWord -ErrorAction Stop Write-RemediationEvent -EventId 4204 -EntryType 'Information' -Message ('disabled: transparency effects for sid path {0}, dwm at {1} engine points' -f $interactiveUserRegistryRoot, [math]::Round($dwmLoad, 0)) $appliedAnyChange = $true } else { Write-Output 'skip: transparency already disabled' } } else { Write-Output 'skip: personalize key not present for this user' } # MinAnimate is read through SystemParametersInfo, which the running session caches. # This sensor is in session 0 as SYSTEM and cannot broadcast a settings change into # the user session, so the registry write is durable but takes effect at the next # sign in rather than immediately. The message says so to avoid over promising $windowMetricsKeyPath = '{0}\Control Panel\Desktop\WindowMetrics' -f $interactiveUserRegistryRoot if (Test-Path $windowMetricsKeyPath) { $currentAnimation = (Get-ItemProperty -Path $windowMetricsKeyPath -Name 'MinAnimate' -ErrorAction SilentlyContinue).MinAnimate if ($currentAnimation -ne '0') { Set-ItemProperty -Path $windowMetricsKeyPath -Name 'MinAnimate' -Value '0' -Type String -ErrorAction Stop Write-RemediationEvent -EventId 4205 -EntryType 'Information' -Message 'disabled: window minimise and maximise animations, applies at next sign in' $appliedAnyChange = $true } else { Write-Output 'skip: window animations already disabled' } } else { Write-Output 'skip: window metrics key not present for this user' } if (-not $appliedAnyChange) { Write-Output 'skip: no composition change applied' }